/**
 * Nettle Hotlist code
 * (C) Nettle developers 2000-2003
 *
 * $Id: hotlist,v 1.29 2003/10/16 20:01:46 archifishal Exp $
 */

#include "generic.h"
#include "globals.h"

#include "hotlist.h"
#include "lineedit.h"
#include "misc.h"
#include "messages.h"
#include "nettle.h"
#include "templ.h"
#include "wimp.h"
#include "wimputil.h"

#include <time.h>
#include <ctype.h>

#define ENTRYHEIGHT (44)

#define BOGUSERROR(what) generror("This error shouldn't happen. (" what ")", false);

typedef enum
{
  ht_invalid,
  ht_telnet,
  ht_ssh,
  ht_task
} hotlisttype;

typedef struct _hotlistnode
{
  struct _hotlistnode *next;
  hotlisttype type;
  char *label;
  char *host;
  char *command;
  unsigned char internal_state;       /* Used for selection etc */
  unsigned char vttype;               /* An index in to the vt type array -- the user gives a string, and we match that to the array at load time */
  unsigned char lineeditor;
  unsigned short terminal_width;
  unsigned short terminal_height;
  unsigned short scrollback;
} hotlistnode;

static hotlistnode *editlisthead = NULL;
static hotlistnode *editlisttail = NULL;
static hotlistnode *hotlisthead = NULL;
static hotlistnode *hotlisttail = NULL;
static hotlistnode *selectednode = NULL;
static int selcon=0, seltype=0, selline=0;
static int edititems = 0;
static int items = 0;
static int *menublock = NULL;

static void int_lose_hotlist(hotlistnode **head);
static void addnodetolist(hotlistnode **head, hotlistnode **tail, hotlistnode *node);
static hotlistnode *hotlist_createnew(void);
static int hotlist_populate_selected(bool);
static void hotlist_clear_states(void);
static hotlistnode *hotlist_createnew(void);

/******************************************************************************/

static void enable_buttons(bool enabled)
{
  int flag = enabled ? 0 : WIMP_ICON_SHADED_BIT;

  set_icon_state( win_panehot, icon_panehot_edit, flag, WIMP_ICON_SHADED_BIT);
  set_icon_state( win_panehot, icon_panehot_copy, flag, WIMP_ICON_SHADED_BIT);
  set_icon_state( win_panehot, icon_panehot_delete, flag, WIMP_ICON_SHADED_BIT);
}

/******************************************************************************/

static char *get_icon_data(int window, int icon)
{
  static char buf[256];

  return read_icon_data(window, icon, buf, 256);
}

/******************************************************************************/

static bool hotlist_copynode(hotlistnode *node, hotlistnode **nodenew)
{
  hotlistnode *newnode = (hotlistnode *) malloc(sizeof(hotlistnode));
  if (!newnode)
    return false;

  *nodenew = newnode;

  newnode->next           = NULL;
  newnode->label	  = NULL;
  newnode->host 	  = NULL;
  newnode->command	  = NULL;
  newnode->internal_state = node->internal_state;
  newnode->type 	  = node->type;
  newnode->vttype	  = node->vttype;
  newnode->lineeditor	  = node->lineeditor;
  newnode->terminal_width = node->terminal_width;
  newnode->terminal_height= node->terminal_height;
  newnode->scrollback	  = node->scrollback;

  if (node->internal_state & 1)
    node->internal_state &= ~1;

  if (node->label)
  {
    newnode->label = malloc(strlen(node->label)+1);
    if (!newnode->label)
      goto err;
    strcpy(newnode->label, node->label);
  }

  if (node->host)
  {
    newnode->host = malloc(strlen(node->host)+1);
    if (!newnode->host)
      goto err;
    strcpy(newnode->host, node->host);
  }

  if (node->command)
  {
    newnode->command = malloc(strlen(node->command)+1);
    if (!newnode->command)
      goto err;
    strcpy(newnode->command, node->command);
  }

  return true;

err:
  free(newnode->label);
  free(newnode->host);
  free(newnode->command);
  free(newnode);
  return false;
}

/******************************************************************************/

static void copy_hotlist(bool edit_to_live) /* true = copy the editlist to the live list */
{
  hotlistnode *node = edit_to_live ? editlisthead : hotlisthead;
  hotlistnode *newnode;

  int_lose_hotlist(edit_to_live ? &hotlisthead : &editlisthead);

  if (edit_to_live)
    items = 0;
  else
    edititems = 0;

  while (node)
  {
    if (hotlist_copynode(node, &newnode) == false)
    {
      /* bleugh!  Whatamess.  Should inform the user */
      return;
    }

    if (edit_to_live)
      addnodetolist(&hotlisthead, &hotlisttail, newnode);
    else
      addnodetolist(&editlisthead, &editlisttail, newnode);

    if (edit_to_live)
      items++;
    else
      edititems++;

    node = node->next;
  }
}

/******************************************************************************/

static int hash_type(hotlisttype *type, const char *value)
{
  if (strncmp(value,"telnet",6)==0)
    *type = ht_telnet;
  else if (strncmp(value,"ssh",3)==0)
    *type = ht_ssh;
  else if (strncmp(value,"task",4)==0)
    *type = ht_task;
  else return 1;

  return 0;
}

/******************************************************************************/

static int hash_term(unsigned char *type, const char *value)
{
  int i;
  *type = 0;
  for(i=0; i<NO_OF_TERMINAL_TYPES; i++)
  {
    if (strcmp(value,terminal_name[i])==0)
    {
      *type = i;
      return 0;
    }
  }

  return 1;
}

/******************************************************************************/

static int read_hot_string(char **dest, char *value, char *check, const char *compare)
{
  if (strncmp(check,compare,strlen(compare))==0)
  {
    if (*dest)
      return 1;
    *dest = malloc(strlen(value)+1);
    if (!*dest)
      return 1;
    strcpy(*dest, value);
  }
  return 0;
}

/******************************************************************************/

static int read_hot_number(unsigned short *dest, char *value, char *check, const char *compare)
{
  if (strncmp(check,compare,strlen(compare))==0)
  {
    unsigned long val = strtoul(value, NULL, 10);
    if (val >= (1<<(8*sizeof(short))) ) /* 65535 */
      return 1;
    *dest = (unsigned short) val;
  }
  return 0;
}

/******************************************************************************/

static int read_hot_present(char *flag, char *value, char *check, const char *compare)
{
  *flag = '\0';
  if (strncmp(check,compare,strlen(compare))==0)
  {
    *flag = 'y';
  }
  return 0;
}

/******************************************************************************/

static void addnodetolist(hotlistnode **head, hotlistnode **tail, hotlistnode *node)
{
  node->next = NULL;
  if (!*head)
    *head = node;
  else
    (*tail)->next = node;
  *tail = node;
}

/******************************************************************************/

void lose_hotlist(void)
{
  int_lose_hotlist(&hotlisthead);
  int_lose_hotlist(&editlisthead);
}

/******************************************************************************/

static void int_lose_hotlist(hotlistnode **head)
{
  while (*head)
  {
    hotlistnode *next = (*head)->next;

    free((*head)->label);
    free((*head)->command);
    free((*head)->host);
    free(*head);

    *head = next;
  }

  free(menublock);
  menublock = NULL;
}

/******************************************************************************/

static char *get_line(FILE *in)
{
  static char string[256];
  char *str;
  int len;

  str = fgets(string, sizeof(string), in);
  if (!str)
   return NULL;

  /* strip CRs, spaces etc off the end */
  len = strlen(string);
  for(;len;len--)
  {
    if (string[len-1]>' ')
      break;
    else
      string[len-1]='\0';
  }

  return string;
}

/******************************************************************************/

static hotlistnode *create_blank(void)
{
  hotlistnode *node = (hotlistnode *) malloc(sizeof(hotlistnode));
  if (!node)
    return NULL;

  node->next = NULL;
  node->internal_state = 0;
  node->type = ht_telnet;
  node->label = NULL;
  node->host = NULL;
  node->command = NULL;

  /* The following pull values from the global choices */
  node->vttype = default_terminal;
  node->lineeditor = line_editor;
  node->terminal_width = defaultsize.x;
  node->terminal_height = defaultsize.y;
  node->scrollback = defaultscroll;

  return node;
}

/******************************************************************************/

static void create_entry(char *title, FILE *in)
{
  char *str,*tail,tmp;
  unsigned short tmpy;
  hotlistnode *node = create_blank();
  if (!node)
    goto error;

  node->label = malloc(strlen(title)+1);
  if (!node->label)
    goto error;

  strcpy(node->label, title);

  str = get_line(in);
  if (!(str[0]=='{' && str[1]=='\0'))
    goto errbarf;

  do
  {
    str = get_line(in);
    if (str)
    {
      if (str[0]=='}')
      {
        addnodetolist(&editlisthead, &editlisttail, node);
        edititems++;
        return;
      }

      if (!(*str=='#' || *str=='\0'))
      {
        while (*str==' ')
          str++;

        if (strlen(str)<6 || str[4]!=':')
          goto errbarf;

        tail = str+5;
        while (*tail==' ')
          tail++;

        if(read_hot_string(&node->host           , tail, str, "host")) goto errbarf;
        if(read_hot_string(&node->command        , tail, str, "cmnd")) goto errbarf;
        if(read_hot_number(&node->terminal_width , tail, str, "wide")) goto errbarf;
        if(read_hot_number(&node->terminal_height, tail, str, "high")) goto errbarf;
        if(read_hot_number(&node->scrollback     , tail, str, "scrl")) goto errbarf;
        tmpy = node->lineeditor;
        if(read_hot_number(&tmpy                 , tail, str, "lned")) goto errbarf;
        node->lineeditor = (char) tmpy;
        switch (choices_file_version_number)
        {
          case 0:
            if (node->lineeditor==LINEEDIT_CHECKBOX_OFF)
              node->lineeditor=LINEEDIT_CHECKBOX_ON;
            else if (node->lineeditor==LINEEDIT_CHECKBOX_ON)
              node->lineeditor=LINEEDIT_ANTTERM;
        }

        if(read_hot_present(&tmp                 , tail, str, "type")) goto errbarf;
          else if (tmp) if (hash_type(&node->type, tail)) goto errbarf;
        if(read_hot_present(&tmp                 , tail, str, "term")) goto errbarf;
          else if (tmp) if (hash_term(&node->vttype, tail)) goto errbarf;

      }
    }
  } while (str);

errbarf:
  generror("BadHotlist", true);
error:
  if (node)
  {
    free(node->label);
    free(node->host);
    free(node->command);
    free(node);
  }
  fseek(in, 0, SEEK_END);

}

/******************************************************************************/

static bool save_hotlist(void)
{
  char hotlist_path[256];
  char string[256];
  hotlistnode *node = hotlisthead;

  get_system_variable(hotlist_path, "Choices$Write", sizeof(hotlist_path));

  if (hotlist_path[0] == '\0' )
  {
    sprintf(string, "<%s$Dir>.Choices", application_name);
  }
  else
  {
    sprintf(string, "<Choices$Write>.%s", application_name);
  }

  _swi(OS_File, _INR(0,1)|_IN(4), 8, string, 0);
  strcat(string, ".Hotlist");

  _kernel_last_oserror();

  {
    bool error=false;
    FILE *file_handle=fopen(string, "w");

    if (file_handle==NULL)
    {
      /* Couldn't open the file for writing */
      error=true;
    }
    else
    {
      time_t current_time;

      time(&current_time);

      fprintf(file_handle, "# Nettle hotlist file\n");
      fprintf(file_handle, "# Created on %s", ctime(&current_time));

      while (node)
      {
        if(node->label && node->label[0])
          fprintf(file_handle, "\n%s\n{\n", node->label);
        else
          fprintf(file_handle, "\n(Unnamed){\n");

        if(node->host && node->host[0])
          fprintf(file_handle, "  host: %s\n", node->host);

        if(node->command && node->command[0])
          fprintf(file_handle, "  cmnd: %s\n", node->command);

        fprintf(file_handle, "  type: %s\n", \
          node->type != ht_task ? node->type != ht_ssh ? \
          "telnet" : "ssh" : "task");

        fprintf(file_handle, "  term: %s\n", terminal_name[node->vttype]);

        fprintf(file_handle, "  wide: %d\n", node->terminal_width);

        fprintf(file_handle, "  high: %d\n", node->terminal_height);

        fprintf(file_handle, "  scrl: %d\n", node->scrollback);

        fprintf(file_handle, "  lned: %d\n", node->lineeditor);

        fprintf(file_handle, "}\n");

        node = node->next;
      }


      if (!ferror(file_handle))
      {
        if (fclose(file_handle))
          error=true;

        file_handle=NULL;
      }
    }

    if (error)
    {
      gensaveerror(file_handle, "CantSaveHotlist");
      return false;
    }

    return true;
  }
}

/******************************************************************************/

void load_hotlist(void)
{
  FILE *file_handle;
  char hotlist_path[256];
  char string[256];
  char *str;

  get_system_variable(hotlist_path, "Choices$Write", sizeof(hotlist_path));

  if (hotlist_path[0] == '\0' )
  {
    sprintf(string, "<%s$Dir>.Choices.Hotlist", application_name);
  }
  else
  {
    sprintf(string, "Choices:%s.Hotlist", application_name);
  }

  file_handle = fopen(string,"r");
  if (!file_handle)
    return;

  do
  {
    str = get_line(file_handle);
    if (str)
    {
      if (!(*str=='#' || *str=='\0'))
      {
        create_entry(str, file_handle);
      }
    }
  } while (str);

  copy_hotlist(true);

  fclose(file_handle);
}

/******************************************************************************/

int *create_hotlist_menu(void)
{
  hotlistnode *node = hotlisthead;
  int position=7;
  int wide=200;

  if (!items)
    return NULL;

  if (!menublock)
  {
    menublock = (int *) malloc(24*items+28);
    if (!menublock)
      return NULL;

    lookup("Hotlist", (char *)menublock, 12);

    menublock[3]=(7<<0)+(2<<8)+(7<<16)+(0<<24); /* black on white items, black on grey title text */
    menublock[4]=wide;
    menublock[5]=44;
    menublock[6]=0;

    while (node)
    {
      menublock[position++]= node->next ? 0 : (1<<7);  /* Set "last" bit if this is the end of the list */
      menublock[position++]=-1;  /* No submenu */

#ifdef HOTLIST_NO_SPRITES
      menublock[position++]=WIMP_ICON_FGCOL(7) | WIMP_ICON_VCENT_BIT | WIMP_ICON_FILLED_BIT | \
  			    WIMP_ICON_TEXT_BIT | WIMP_ICON_INDIRECTED_BIT;

      menublock[position++]=(int) node->label;
      menublock[position++]=0;
      menublock[position++]=strlen(node->label)+1;
#else
      menublock[position++]=WIMP_ICON_FGCOL(7) | WIMP_ICON_VCENT_BIT | WIMP_ICON_FILLED_BIT | \
  			    WIMP_ICON_TEXT_BIT | WIMP_ICON_INDIRECTED_BIT | WIMP_ICON_SPRITE_BIT;

      menublock[position++]=(int) node->label;
      menublock[position++]=(int)(node->type == ht_ssh ? "snettle_ssh" : \
                                  node->type == ht_telnet ? "snettle_telnt" :
                                  "snettle_task");
      menublock[position++]=strlen(node->label)+1;
#endif

    if (menublock[position-1]+1 * 16 > wide)
      wide =  menublock[position-1]+1 * 16;

      node = node->next;
    }

    menublock[4] = wide;
  }
  return (int *) menublock;
}

/******************************************************************************/

static hotlistnode *decode_hotlist_menu(hotlistnode *node, int entry)
{
  if (entry<0 || entry>=((node==hotlisthead)?items:edititems))
    return NULL;

  for(;entry>0;entry--)
    node = node->next;

  return node;
}

/******************************************************************************/

void hotlist_draw(struct wimp_getrectangle_block *getrect_block)
{
  struct wimp_ploticon_block pib;
  int y=-4;
  hotlistnode *node = editlisthead;

  pib.min.x = 12;
  pib.max.x = 1024;
  pib.icon_flags = WIMP_ICON_TEXT_BIT | WIMP_ICON_SPRITE_BIT | WIMP_ICON_VCENT_BIT | \
                   WIMP_ICON_INDIRECTED_BIT | WIMP_ICON_FILLED_BIT | WIMP_ICON_FGCOL(7);

  for(;node;node=node->next)
  {
    pib.max.y = y;
    pib.min.y = y - ENTRYHEIGHT;
    y -= ENTRYHEIGHT;

    pib.contents.it.text = node->label;
    pib.contents.it.validation = (char *)(int) (node->type == ht_ssh ? "snettle_ssh" : \
                                 node->type == ht_telnet ? "snettle_telnt" :
                                 "snettle_task");

    if (node->internal_state & 1)
      pib.icon_flags |= WIMP_ICON_BGCOL(2);
    else
      pib.icon_flags &= ~WIMP_ICON_BGCOL((unsigned int)15);

    pib.contents.it.text_len = strlen(pib.contents.it.text)+1;
    _swi(Wimp_PlotIcon, _IN(1), &pib);
  }
}

/******************************************************************************/

int hotlist_num_items(void)
{
  return items;
}

/******************************************************************************/

static void hotlist_clear_states(void)
{
  hotlistnode *node = editlisthead;

  for(;node;node=node->next)
  {
    node->internal_state &= ~1;
  }
  enable_buttons(false);
}

/******************************************************************************/

static hotlistnode *hotlist_unlink_selected(void)
{
  hotlistnode *node = editlisthead;
  hotlistnode *prev = NULL;

  for(;node;node=node->next)
  {
    if(node->internal_state & 1)
    {
      if (prev)
        prev->next = node->next;
      else
      {
        editlisthead = node->next;
	prev = editlisthead;
      }

      if (editlisttail)
      {
        if (editlisttail == node)
          editlisttail = prev;
      }

      edititems--;
      return node;
    }
    prev = node;
  }
  BOGUSERROR("Unlink: no item selected");
  return NULL;
}

/******************************************************************************/

static void hotlist_delete_selected(void)
{
  hotlistnode *node = hotlist_unlink_selected();

  if (node)
  {
    free(node->label);
    free(node->command);
    free(node->host);
    free(node);
  }
}

/******************************************************************************/

static hotlistnode *hotlist_get_selected(void)
{
  hotlistnode *node = editlisthead;

  for(;node;node=node->next)
  {
    if(node->internal_state & 1)
    {
      return node;
    }
  }

  return NULL;
}

/******************************************************************************/

static void hotlist_copy_selected(void)
{
  hotlistnode *node = hotlist_get_selected();

  if (node)
  {
    hotlistnode *pop;
    if (hotlist_copynode(node, &pop) == true)
    {
      addnodetolist(&editlisthead, &editlisttail, pop);
      edititems++;
    }
    return;
  }

  _swi(0x107,0);
}

/******************************************************************************/

static bool copy_hotedit_to_selected(void)
{
  char _label[100],*label=_label;
  char _host[256],*host=_host;
  char _command[256],*command=_command;
  char *tmp;
  int width, height, scrollback;
  hotlistnode *node = selectednode;

  if (!selectednode)
    return true;  /* Shouldn't be possible */

  read_icon_data(win_hotedit, icon_hotedit_label, label, 100);
  while (*label==' ')
    label++;

  if(*label<' ')
  {
    generror("HotBlankLabel",true);
    return false;
  }

  read_icon_data(win_hotedit, icon_hotedit_hostname, host, 256);
  while (*host==' ')
    host++;

  if(selcon != ht_task)
  if(*host<' ')
  {
    generror("HotBlankHost",true);
    return false;
  }

  read_icon_data(win_hotedit, icon_hotedit_command, command, 256);
  while (*command==' ')
    command++;

  if(selcon == ht_task)
  if(*command<' ')
  {
    generror("HotBlankCommand",true);
    return false;
  }

  tmp = get_icon_data(win_hotedit, icon_hotedit_termwidth);
  width = atoi(tmp);

  if (width<2 || width>65535)
  {
    generror("HotBadWidth",true);
    return false;
  }


  tmp = get_icon_data(win_hotedit, icon_hotedit_termheight);
  height = atoi(tmp);

  if (height<2 || height>65535)
  {
    generror("HotBadHeight",true);
    return false;
  }


  tmp = get_icon_data(win_hotedit, icon_hotedit_scrollback);
  scrollback = atoi(tmp);

  if (scrollback<0 || scrollback>65535)
  {
    generror("HotBadScroll",true);
    return false;
  }


  free(node->label);
  node->label = strdup(label);
  assert(node->label);

  free(node->host);
  node->host = strdup(host);
  assert(node->host);

  free(node->command);
  node->command = strdup(command);
  assert(node->command);

  node->terminal_width = width;
  node->terminal_height = height;
  node->scrollback = scrollback;

  node->type = (hotlisttype) selcon;
  node->vttype = seltype;
  node->lineeditor = selline;

  return true;
}

/******************************************************************************/

static void do_grey_hotedit(int contype)
{
  switch (contype)
  {
    case ht_telnet:
      /* unshade the host icons, shade the command icons */
      set_icon_state( win_hotedit, icon_hotedit_hostname, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_hostlabel, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_command, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_commandlabel, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
      break;

    case ht_ssh:
       /* unshade the host and command icons */
      set_icon_state( win_hotedit, icon_hotedit_hostname, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_hostlabel, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_command, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_commandlabel, 0, WIMP_ICON_SHADED_BIT);
      break;

    case ht_task:
      /* shade the host icons, unshade the command icons */
      set_icon_state( win_hotedit, icon_hotedit_hostname, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_hostlabel, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_command, 0, WIMP_ICON_SHADED_BIT);
      set_icon_state( win_hotedit, icon_hotedit_commandlabel, 0, WIMP_ICON_SHADED_BIT);
      break;
  }

  switch (contype)
  {
    case ht_telnet:
    case ht_ssh:
      set_caret_position(win_hotedit, icon_hotedit_hostname, -1,
                         get_icon_data_length(win_hotedit, icon_hotedit_hostname));
      break;

    case ht_task:
      set_caret_position(win_hotedit, icon_hotedit_command, -1,
                         get_icon_data_length(win_hotedit, icon_hotedit_command));
      break;
  }

}

/******************************************************************************/

static int hotlist_populate_selected(bool get_selected)
{
  char tmp[20];

  if (get_selected)
  {
    selectednode = hotlist_get_selected();
  }

  if(!selectednode)
    return 0;

  set_icon_data(win_hotedit, icon_hotedit_label, selectednode->label ? selectednode->label : "<unnamed>");
  set_icon_data(win_hotedit, icon_hotedit_hostname, selectednode->host ? selectednode->host : "");
  set_icon_data(win_hotedit, icon_hotedit_command, selectednode->command ? selectednode->command : "");
  sprintf(tmp,"%d",selectednode->terminal_width);
  set_icon_data(win_hotedit, icon_hotedit_termwidth, tmp);
  sprintf(tmp,"%d",selectednode->terminal_height);
  set_icon_data(win_hotedit, icon_hotedit_termheight, tmp);
  sprintf(tmp,"%d",selectednode->scrollback);
  set_icon_data(win_hotedit, icon_hotedit_scrollback, tmp);

  set_icon_data(win_hotedit, icon_hotedit_contype, lookup_static(\
      selectednode->type != ht_task ? selectednode->type != ht_ssh ? \
      "Telnet" : "SSH" : "Taskwindow") );
  selcon = selectednode->type;

  set_icon_data(win_hotedit, icon_hotedit_termtype, terminal_name[selectednode->vttype]);
  seltype = selectednode->vttype;

  sprintf(tmp,"LineEd%d", selectednode->lineeditor);
  set_icon_data(win_hotedit, icon_hotedit_lineedittype, lookup_static(tmp));
  selline = selectednode->lineeditor;

  return 1;
}

/******************************************************************************/

static hotlistnode *hotlist_createnew(void)
{
  hotlistnode *node = create_blank();
  if (!node)
    return NULL;

  free(menublock);
  menublock = NULL;

  node->label = malloc(100);
  if (!node->label)
    goto err;
  node->command = malloc(256);
  if (!node->label)
    goto err;
  node->host = malloc(256);
  if (!node->label)
    goto err;

  strcpy(node->label, "New item");
  node->command[0]='\0';
  node->host[0]='\0';

  addnodetolist(&editlisthead, &editlisttail, node);
  edititems++;

  return node;

err:
  free(node->label);
  free(node->command);
  free(node->host);
  free(node);

  return NULL;
}

/******************************************************************************/

void win_panehot_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_panehot_new:
      {
        hotlistnode *node = hotlist_createnew();
        hotlist_clear_states();
        node->internal_state |= 1;
      }
      hotlist_populate_selected(true);
      open_window_centred(win_hotedit);
      do_grey_hotedit(selcon);
      _swi(Wimp_SetCaretPosition, _INR(0,5), win_hotedit, icon_hotedit_label, \
        -1, -1, -1, get_icon_data_length(win_hotedit, icon_hotedit_label));
      resize_hotpane();
      force_redraw(win_hotpane,0,-(1<<28),1024,0);
      {
        struct wimp_getwindowstate_block window;
        window.window_handle=win_hotpane;
        _swi(Wimp_GetWindowState, _IN(1), &window);
        window.scroll.y=-(1<<28);
        _swi(Wimp_OpenWindow, _IN(1), &window);
      }
      break;

    case icon_panehot_edit:
      if (hotlist_populate_selected(true))
      {
        open_window_centred(win_hotedit);
        do_grey_hotedit(selcon);
        _swi(Wimp_SetCaretPosition, _INR(0,5), win_hotedit, icon_hotedit_label, \
          -1, -1, -1, get_icon_data_length(win_hotedit, icon_hotedit_label));
      }
      break;

    case icon_panehot_copy:
      close_window(win_hotedit);
      if (hotlist_populate_selected(true))
      {
        hotlist_copy_selected();
    	resize_hotpane();
    	force_redraw(win_hotpane,0,-(1<<28),1024,0);
    	{
    	  struct wimp_getwindowstate_block window;
    	  window.window_handle=win_hotpane;
    	  _swi(Wimp_GetWindowState, _IN(1), &window);
	  window.scroll.y = -(1<<28);
    	  _swi(Wimp_OpenWindow, _IN(1), &window);
    	}
      }
      break;

    case icon_panehot_delete:
      close_window(win_hotedit);
      if (hotlist_populate_selected(true))
      {
        if (generror_question("DeleteHotButtons","DeleteHot",0))
        {
          hotlist_delete_selected();
          resize_hotpane();
          force_redraw(win_hotpane,0,-(1<<28),1024,0);
          {
            struct wimp_getwindowstate_block window;
            window.window_handle=win_hotpane;
            _swi(Wimp_GetWindowState, _IN(1), &window);
            _swi(Wimp_OpenWindow, _IN(1), &window);
          }
        }
      }
      break;

  }
}

/******************************************************************************/

static void hotlist_drawitem(hotlistnode *draw)
{
  int y=-4;
  hotlistnode *node = editlisthead;

  for(;node;node=node->next, y -= ENTRYHEIGHT)
  {
    if (node == draw)
    {
      force_redraw(win_hotpane,0,y-ENTRYHEIGHT,1024,y);
      return;
    }
  }
}

/******************************************************************************/

void win_hotpane_click(int x, int y, int buttons, int icon_handle)
{
  int item, y_pos;
  hotlistnode *node, *sel;
  struct wimp_getwindowstate_block gwsblock;

  gwsblock.window_handle=win_hotpane;
  _swi(Wimp_GetWindowState, _IN(1), &gwsblock);
  y_pos=gwsblock.max.y-gwsblock.scroll.y;

  item = -((y - y_pos) + 4) / ENTRYHEIGHT;

  node = decode_hotlist_menu(editlisthead, item);
  if (!node)
  {
    _swi(0x107, 0);
    return;
  }

  switch(buttons)
  {
  case ClickSelect:
  case DoubleSelect:
  case DragSelect:
    sel = hotlist_get_selected();
    if(node == sel)
      if (buttons == ClickSelect) return;

    if (sel && node!=sel)
    {
      sel->internal_state &= ~1;
      hotlist_drawitem(sel);
    }
    if (!(node->internal_state & 1))
    {
      node->internal_state |= 1;
      hotlist_drawitem(node);
      enable_buttons(true);
    }
    switch (buttons)
    {
    case DoubleSelect:
      win_panehot_click(0,0,4,icon_panehot_edit);
      break;

    case DragSelect:
      {
        struct wimp_dragbox_block dragblock;
        struct wimp_autoscroll_block scrollblock;

        dragblock.window_handle = win_hotpane;
        dragblock.drag_type = 5; /* Fixed size box */
        dragblock.parent_min.x = gwsblock.min.x;
        dragblock.parent_min.y = gwsblock.min.y - ENTRYHEIGHT;
        dragblock.parent_max.x = gwsblock.max.x;
        dragblock.parent_max.y = gwsblock.max.y + ENTRYHEIGHT;
        dragblock.min.x = gwsblock.min.x ;
        dragblock.min.y = gwsblock.max.y -(item * ENTRYHEIGHT) - 4 - ENTRYHEIGHT - gwsblock.scroll.y;
        dragblock.max.y = dragblock.min.y + ENTRYHEIGHT;
        dragblock.max.x = dragblock.min.x + (gwsblock.max.x - gwsblock.min.x );

        _swi(Wimp_DragBox, _INR(1,3), &dragblock, 0x4b534154, 3);
        drag_window_handle = win_hotpane;

        scrollblock.window_handle = win_hotpane;
        scrollblock.left_pause = 0;
        scrollblock.right_pause = 0;
        scrollblock.bottom_pause = ENTRYHEIGHT*2;
        scrollblock.top_pause = ENTRYHEIGHT*2;
        scrollblock.pause_duration = 0; /* no pause time */
        scrollblock.statechange_handler = 1; /* Use Wimp pointer shape routine */
        _swix(Wimp_AutoScroll, _INR(0,1), 2, &scrollblock);
      }
      break;
    }
    break;

  case ClickAdjust:
    if (node->internal_state & 1)
    {
      node->internal_state &= ~1;
      hotlist_drawitem(node);
      enable_buttons(false);
    }
    break;

  }
}

/******************************************************************************/

void hotlist_dragdone(void)
{
  int item, y_pos;
  struct wimp_getwindowstate_block gwsblock;
  struct wimp_getpointerinfo_block wgpi;
  hotlistnode *node,*sel;

  _swi(Wimp_DragBox, _IN(1), -1); /* Stop dragging */
  _swix(Wimp_AutoScroll, _IN(0), 0); /* Stop auto scrolling */

  sel = hotlist_get_selected();

  if (!sel)
   return; /* shouldn't be possible! */

  gwsblock.window_handle=win_hotpane;
  _swi(Wimp_GetWindowState, _IN(1), &gwsblock);
  y_pos=gwsblock.max.y-gwsblock.scroll.y;

  _swi(Wimp_GetPointerInfo, _IN(1), &wgpi);
  item = -((wgpi.pos.y - y_pos) + 4) / ENTRYHEIGHT;

  node = decode_hotlist_menu(editlisthead, item);

  if (node == sel || edititems<2)
    return; /* Dropped the thing back on itself, so don't do anything */

  if (node==NULL || node==editlisthead)
  {
    if (item<=0)
    {
      if (!hotlist_unlink_selected())
        return; /* Shouldn't happen */
      sel->next = editlisthead;
      editlisthead = sel;
    }
    else
    {
      if (!hotlist_unlink_selected())
        return; /* Shouldn't happen */
      if (editlisttail)
        editlisttail->next = sel;
      editlisttail = sel;
      sel->next = NULL;
    }
  }
  else
  {
    if (!hotlist_unlink_selected())
      return; /* Shouldn't happen */
    sel->next = node->next;
    node->next = sel;
    if (node==editlisttail)
      editlisttail = sel;
  }

  edititems++;

  force_redraw(win_hotpane,0,-(1<<28),1024,0);
}

/******************************************************************************/

void win_hotedit_click(int x, int y, int buttons, int icon_handle)
{
  switch(icon_handle)
  {
  case icon_hotedit_cancel:
    if (buttons==1)
    {
      hotlist_populate_selected(false);
    }
    else
    {
      close_window(win_hotedit);
    }
    break;

  case icon_hotedit_update:
    if(copy_hotedit_to_selected())
    {
      if (buttons==4)
      {
        close_window(win_hotedit);
      }
      force_redraw(win_hotpane,0,-(1<<28),1024,0);
    }
    break;

  case icon_hotedit_delete:
    if (generror_question("DeleteHotButtons","DeleteHot",0))
    {
      close_window(win_hotedit);
      hotlist_delete_selected();
      resize_hotpane();
      force_redraw(win_hotpane,0,-(1<<28),1024,0);
      {
        struct wimp_getwindowstate_block window;
        window.window_handle=win_hotpane;
        _swi(Wimp_GetWindowState, _IN(1), &window);
        _swi(Wimp_OpenWindow, _IN(1), &window);
      }
    }
    break;

  case icon_hotedit_lineeditbut:
    create_lineeditor_type_menu(win_hotedit, icon_hotedit_lineedittype, icon_hotedit_lineeditbut);
    break;

  case icon_hotedit_contypebut:
    create_connection_type_menu(win_hotedit, icon_hotedit_contype, icon_hotedit_contypebut);
    break;

  case icon_hotedit_termtypebut:
    create_terminal_type_menu(win_hotedit, icon_hotedit_termtype, icon_hotedit_termtypebut);
    break;
  }
}

/******************************************************************************/

void resize_hotpane(void)
{
  struct wimp_getwindowinfo_block window;
  struct wimp_setextent_block ext;
  window.window_handle=win_hotpane;

  _swi(Wimp_GetWindowInfo, _IN(1), 1 + (int) &window); /* no icons */
  ext.min.x = 0;
  ext.max.y = 0;
  ext.max.x = window.max.x - window.min.x;
  ext.min.y = -(edititems * ENTRYHEIGHT + 16);
  if (ext.min.y > -(window.max.y - window.min.y))
    ext.min.y = -(window.max.y - window.min.y);

  _swi(Wimp_SetExtent,_INR(0,1),win_hotpane, &ext);

  enable_buttons(hotlist_get_selected()?true:false);
}

/******************************************************************************/

void fire_hotlist(int item)
{
  hotlistnode *hot = decode_hotlist_menu(hotlisthead, item);
  struct connection_params params;

  if (!hot)
    return;

  params.terminal_type    = (enum nettle_terminal) hot->vttype;
  params.host             = hot->host;
  params.port             = 0;
  params.command          = hot->command;
  params.width            = hot->terminal_width;
  params.height           = hot->terminal_height;
  params.scrollback       = hot->scrollback;
  params.line_editor_type = hot->lineeditor;
  params.login_user       = NULL;
  params.login_pass       = NULL;
  strcpy(params.label, hot->label);

  switch(hot->type)
  {
    case ht_telnet:
      params.connection_type = NETTLE_TELNET;
      break;

    case ht_ssh:
      params.connection_type = NETTLE_SSH;
      break;

    case ht_task:
      params.connection_type = NETTLE_TASKWINDOW;
      break;

    default:
      return;
  }

  start_connection_friedport(&params);
}

/******************************************************************************/

bool finish_hotlist_editing(int dowhat) /* 0=cancel, 1=set, 2=save */
{
  if (dowhat<0 || dowhat>2)
  {
    BOGUSERROR("Finish: bad code");
    return false;
  }

  copy_hotlist(dowhat ? true : false);

  if (dowhat == 2)
  {
    return save_hotlist();
  }

  return true;
}

/******************************************************************************/

void hotlist_inform(int icon, int menuselection)
{
  if (menuselection<0)
    return;

  switch(icon)
  {
    case icon_hotedit_lineedittype:
      selline = menuselection;
      break;

    case icon_hotedit_contype:
      selcon = menuselection+1;
      do_grey_hotedit(selcon);
      break;

    case icon_hotedit_termtype:
      seltype = menuselection;
      break;

    default:
      BOGUSERROR("Inform: Bad icon");
      break;
  }
}
